from openai import OpenAI
from dotenv import load_dotenv, find_dotenv

"""
准备工作
"""
_ = load_dotenv(find_dotenv()) # 1.加载 .env 文件中定义的环境变量(OPENAI_API_KEY)
client = OpenAI()  # # 2. 初始化 OpenAI 客户端 (client 类对象) ,默认使用环境变量中的 OPENAI_API_KEY 和 OPENAI_BASE_URL

def get_completion(prompt, response_format="text", model="deepseek-r1"):
    messages = [{"role": "user", "content": prompt}]    # 将 prompt 作为用户输入
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=1,                                  # 模型输出的随机性，0 表示随机性最小
        stream=True,
        response_format={"type": response_format},   # 返回消息的格式，text 或 json_object

    )
    # return response.choices[0].message.content
    return response


# 任务描述
instruction = """你的任务是识别用户对手机流量套餐产品的选择条件。每种流量套餐产品包含三个属性：名称，月费价格，月流量。根据用户输入，识别用户在上述三种属性上的需求是什么。"""
# 用户输入
input_text = """办个100G的套餐。"""
# 约定输出格式:更精细，定义约束
output_format = """以 JSON 格式输出
1. name字段的取值为string类型，取值必须为以下之一：经济套餐、畅游套餐、无限套餐、校园套餐 或 null；

2. price字段的取值为一个结构体 或 null，包含两个字段：
(1) operator, string类型，取值范围：'<='（小于等于）, '>=' (大于等于), '=='（等于）
(2) value, int类型

3. data字段的取值为取值为一个结构体 或 null，包含两个字段：
(1) operator, string类型，取值范围：'<='（小于等于）, '>=' (大于等于), '=='（等于）
(2) value, int类型或string类型，string类型只能是'无上限'

4. 用户的意图可以包含按price或data排序，以sort字段标识，取值为一个结构体：
(1) 结构体中以"ordering"="descend"表示按降序排序，以"value"字段存储待排序的字段
(2) 结构体中以"ordering"="ascend"表示按升序排序，以"value"字段存储待排序的字段

输出中只包含用户提及的字段，不要猜测任何用户未直接提及的字段，不输出值为null的字段。"""

# 为了提高准确性增加例子
examples = """
便宜的套餐：{"sort":{"ordering"="ascend","value"="price"}}
有没有不限流量的：{"data":{"operator":"==","value":"无上限"}}
流量大的：{"sort":{"ordering"="descend","value"="data"}}
100G以上流量的套餐最便宜的是哪个：{"sort":{"ordering"="ascend","value"="price"},"data":{"operator":">=","value":100}}
月费不超过200的：{"price":{"operator":"<=","value":200}}
就要月费180那个套餐：{"price":{"operator":"==","value":180}}
经济套餐：{"name":"经济套餐"}
土豪套餐：{"name":"无限套餐"}
"""
# 增加思维链 提高准确性
cot = "请一步一步分析对话"

# 多轮对话上下文
context = f"""
客服：有什么可以帮您
用户：有什么100G以上的套餐推荐
客服：我们有畅游套餐和无限套餐，您有什么价格倾向吗
用户：{input_text}
"""



# prompt 模版。instruction 和 input_text 会被替换为上面的内容
prompt = f"""
# 目标{instruction}
{cot}
# 用户输入{input_text}
# 输出格式{output_format}
# 举例{examples}
# 对话上下文{context}

"""

print("==== Prompt ====")
print(prompt)
print("================")

"""
# 调用大模型 stream=False
1.stream=False 一次性给出结果，返回的是类对象<class 'openai.types.chat.chat_completion.ChatCompletion'>
# ChatCompletion(id='chatcmpl-61141d22-f88a-9390-83d3-4af63ea243ad', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='用户的输入中明确提到了需要100G的流量套餐，因此可以识别出以下需求：\n\n- **名称**：未指定  \n- **月费价格**：未指定  \n- **月流量**：100G  \n\n其他属性（名称、月费价格）用户未提及，因此视为无明确需求。', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=None, reasoning_content='好的，用户输入的是“办个100G的套餐”。首先，我需要分析这句话里的关键信息。用户明确提到了“100G”，这显然是指月流量，所以月流量需求是100G。接下来看名称和月费价格有没有被提到。用户没有具体提到套餐的名称，比如有没有指定什么名称的套餐，比如“畅游套餐”或者“超值包”之类的，所以名称这里应该是无明确需求。月费价格方面，用户也没有提到价格范围或者具体的金额，所以月费价格的条件也是无。因此，用户的关注点主要集中在月流量上，要求100G。需要确认是否有其他隐含的信息，但根据现有的输入，只能确定这三个属性中的月流量有明确需求，其他两个属性用户没有提及，所以是未指定。最后检查有没有可能的误解，比如用户可能提到的“100G”是否指其他属性，但根据常识，流量套餐通常用G来表示流量，所以应该是正确的。总结下来，用户的需求是月流量100G，其他两个属性没有要求。'))], created=1745802473, model='deepseek-r1', object='chat.completion', service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=284, prompt_tokens=60, total_tokens=344, completion_tokens_details=None, prompt_tokens_details=None))
    #1.choices 这是一个列表，通常只包含一个元素，代表模型生成的回复消息。列表中的每个元素是一个 Choice 对象，包含了回复的详细信息。
    #2. usage 该属性包含了本次 API 调用所使用的令牌信息，如 prompt_tokens（提示部分使用的令牌数）、completion_tokens（生成回复使用的令牌数）和 total_tokens（总共使用的令牌数）。
    #3. id 这是本次 API 调用的唯一标识符，可用于跟踪和调试请求
assistant_message = response.choices[0].message.content  #取值得出： <class 'str'>

# 调用大模型：
    response = get_completion(prompt,response_format="json_object")
    assistant_message = response.choices[0].message.content
    print(assistant_message)

# 调用大模型 stream=True
2.stream=True 流式返回数据，返回值是一个生成器对象（generator），你可以通过遍历这个生成器来获取每个数据块
    response = get_completion(prompt,response_format="json_object")
    # 用于存储完整的回复内容
    full_response = ""
    # 遍历生成器对象，处理每个数据块
    for chunk in response:
        # 检查是否有 choices 字段
        if 'choices' in chunk and chunk['choices']:
            # 提取第一个 choice
            choice = chunk['choices'][0]
            # 检查是否有 delta 字段
            if 'delta' in choice:
                delta = choice['delta']
                # 检查是否有 content 字段
                if 'content' in delta:
                    content = delta['content']
                    full_response += content
                    print(content, end='', flush=True)

    print("\n完整回复内容：", full_response)
"""

# 调用大模型
# response = get_completion(prompt,response_format="json_object")
# print(response)  # 返回了一个生成器对象(<class 'openai.Stream'>) <openai.Stream object at 0x0000020D91709400>
print("***********************")


# # 用于存储完整的回复内容
# full_response = ""
# # 遍历生成器对象，处理每个数据块
# for chunk in response:
#     # 检查是否有 choices 字段
#     if 'choices' in chunk and chunk['choices']:
#         # 提取第一个 choice
#         choice = chunk['choices'][0]
#         # 检查是否有 delta 字段
#         if 'delta' in choice:
#             delta = choice['delta']
#             # 检查是否有 content 字段
#             if 'content' in delta:
#                 content = delta['content']
#                 full_response += content
#                 print(content, end='', flush=True)
#
# print("\n完整回复内容：", full_response)
